import gevent
import time 

def commonsubset(sid, pid, N, f, rbc_out, aba_in, aba_out):
    """The BKR93 algorithm for asynchronous common subset.

    :param pid: my identifier
    :param N: number of nodes
    :param f: fault tolerance
    :param rbc_out: an array of :math:`N` (blocking) output functions,
        returning a string
    :param aba_in: an array of :math:`N` (non-blocking) functions that
        accept an input bit
    :param aba_out: an array of :math:`N` (blocking) output functions,
        returning a bit
    :return: an :math:`N`-element array, each element either ``None`` or a
        string
    """
    assert len(rbc_out) == N
    assert len(aba_in) == N
    assert len(aba_out) == N
    byzatine = N[:-f]

    # aba_inputted = [False] * N
    aba_inputted = {key: -1 for key in range(N)}


    aba_values = [0] * N
    rbc_values = [None] * N

    print("this is the commonsubset round",sid.split(":")[1][0] )

    def _recv_rbc(j):
        # Receive output from reliable broadcast
        rbc_values[j] = rbc_out[j]()
        # print("this is thre rbc_value[j]", rbc_values[j])

        # if not aba_inputted[j]:
        #     # Provide 1 as input to the corresponding bin agreement
        #     aba_inputted[j] = True
        #     aba_in[j](1)
        if pid >= 2*f+1:
            aba_inputted[j] = 0
            aba_in[j](0)

        if aba_inputted[j] == -1:
            aba_inputted[j] = 1
            aba_in[j](1)

        

        count_of_ones = sum(1 for value in aba_inputted.values() if value == 1)
        keys_with_uninput = [key for key, value in aba_inputted.items() if value == -1]
        if count_of_ones >= N - f:
            for key in keys_with_uninput:
                aba_inputted[key] = 0
                aba_in[key](0)


        # print('this is the aba_in and pid', count_of_ones, j, pid)
        # print('this is the aba_in and pid', keys_with_uninput, pid)

    r_threads = [gevent.spawn(_recv_rbc, j) for j in range(N)]

    def _recv_aba(j):
        # Receive output from binary agreement
        aba_values[j] = aba_out[j]()  # May block
        # print (pid, j, 'ENTERING CRITICAL', sum(aba_values))
        if sum(aba_values) >= N - f:
            # Provide 0 to all other aba
            for k in range(N):
                # if not aba_inputted[k]:
                if aba_inputted[k] == -1:
                    # aba_inputted[k] = True
                    aba_inputted[k] = 1
                    aba_in[k](0)
                    # print(pid, 'ABA[%d] input -> %d' % (k, 0))
        # print (pid, j, 'EXITING CRITICAL')

    # Wait for all binary agreements
    a_threads = [gevent.spawn(_recv_aba, j) for j in range(N)]
    gevent.joinall(a_threads)
    # print ("aba values of node %d" % pid, aba_values)

    assert sum(aba_values) >= N - f  # Must have at least N-f committed
    print('this the sum(aba_value)',sum(aba_values))
    # print('the fir aba:',time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime()))
    # Wait for the corresponding broadcasts
    for j in range(N):
        if aba_values[j]:
            r_threads[j].join()
            assert rbc_values[j] is not None
        else:
            r_threads[j].kill()
            rbc_values[j] = None

    print('aba values', aba_values)
    return tuple(rbc_values)
